/*
 * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Codename One designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *  
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 * 
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Please contact Codename One through http://www.codenameone.com/ if you 
 * need additional information or have any questions.
 */
package com.codename1.payment;

import com.codename1.io.Externalizable;
import com.codename1.io.Util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Encapsulates a receipt for an in-app purchase.
 * @author shannah
 */
public class Receipt implements Externalizable {
    
    public static final String STORE_CODE_ITUNES="itunes";
    public static final String STORE_CODE_PLAY="play";
    public static final String STORE_CODE_WINDOWS="windows";
    public static final String STORE_CODE_SIMULATOR="simulator";

    /**
     * @param aExternalizableRegistered the externalizableRegistered to set
     */
    private static void setExternalizableRegistered(boolean aExternalizableRegistered) {
        externalizableRegistered = aExternalizableRegistered;
    }
    
    /**
     * The product SKU
     */
    private String sku;
    
    /**
     * The expiry date of the receipt, in case this is a subscription receipt.
     */
    private Date expiryDate;
    
    /**
     * Cancellation date of the receipt, if it has been cancelled.  A cancelled
     * receipt should be treated as if the purchase had never been made in the 
     * first place.
     */
    private Date cancellationDate;
    
    /**
     * Date of purchase.
     */
    private Date purchaseDate;
    
    /**
     * Quantity.  Currently this will always be 1, but potentially could be used
     * internally to denote other quantities.
     */
    private int quantity;
    
    /**
     * The store-specific transaction ID.  This will match exactly the transaction
     * id of the store the issued the receipt.  Use the {@code storeCode} to differentiate
     * between transactions of different stores, which may potentially have the 
     * same transaction Ids.
     */
    private String transactionId;
    
    /**
     * The order data that may be used to verify the receipt with the appropriate
     * REST service.  This data will be in different formats for different stores.
     * 
     * <p>iTunes has a base64 encoded string, Play has a JSON structure, Windows
     * has an XML string, etc...</p>
     * 
     * <p>This will be filled in at the time that the receipt is generated by the
     * platform's implementation, so developers should never have to explicitly 
     * set this.</p>
     */
    private String orderData;
    
    /**
     * The store code from which this receipt originated.  Will usually be one of
     * {@link #STORE_CODE_ITUNES}, {@link #STORE_CODE_PLAY}, {@link #STORE_CODE_WINDOWS},
     * or {@link #STORE_CODE_SIMULATOR}, but developers could also use custom codes
     * if they use a different store, or no store at all.  This code can be used
     * for validating the receipt.
     */
    private String storeCode;
    
    /**
     * An internal ID that may be optionally used to link the receipt to an internal
     * order ID.
     */
    private String internalId;
    
    
    private static boolean externalizableRegistered;

    public Receipt() {
    }

    /**
     * Convenience constructor 
     */
    public Receipt(String sku, Date expiryDate, Date cancellationDate,
        Date purchaseDate, int quantity, String transactionId,
        String orderData, String storeCode, String internalId) {
        this.sku = sku;
        this.expiryDate = expiryDate;
        this.cancellationDate = cancellationDate;
        this.purchaseDate = purchaseDate;
        this.quantity = quantity;
        this.transactionId = transactionId;
        this.orderData = orderData;
        this.storeCode = storeCode;
        this.internalId = internalId;
    }
    
    /**
     * @return the sku
     */
    public String getSku() {
        return sku;
    }

    /**
     * @param sku the sku to set
     * @see #getSku() 
     */
    public void setSku(String sku) {
        this.sku = sku;
    }

    /**
     * The expiry date of the receipt, in case this is a subscription receipt.
     * 
     * <p><strong>NOTE:</strong> The expiry date will NOT be set automatically when the
     * receipt is generated by the platform.  It should be set by the {@link ReceiptStore} in the 
     * {@link ReceiptStore#submitReceipt(com.codename1.payment.Receipt, com.codename1.util.SuccessCallback) } step,
     * and usually on the server-side (in cases where a server is being used to track subscriptions), then the expiry
     * date will be made available when the receipt is loaded using {@link ReceiptStore#fetchReceipts(com.codename1.util.SuccessCallback) }.</p>
     * @return the expiryDate Will be null for non-subscriptions.  
     * 
     */
    public Date getExpiryDate() {
        return expiryDate;
    }

    /**
     * @param expiryDate the expiryDate to set
     * @see #getExpiryDate() 
     */
    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }

    /**
     * {@inheritDoc}
     */
    public int getVersion() {
        return 1;
    }

    
    /**
     * {@inheritDoc}
     */
    public void externalize(DataOutputStream out) throws IOException {
        Map m = new HashMap();
        m.put("sku", getSku());
        m.put("expiryDate", getExpiryDate());
        m.put("cancellationDate", getCancellationDate());
        m.put("purchaseDate", getPurchaseDate());
        m.put("orderData", getOrderData());
        m.put("transactionId", getTransactionId());
        m.put("quantity", getQuantity());
        m.put("storeCode", getStoreCode());
        m.put("internalId", getInternalId());

        Util.writeObject(m, out);
        
    }

    /**
     * {@inheritDoc}
     */
    public void internalize(int version, DataInputStream in) throws IOException {
        Map m = (Map)Util.readObject(in);
        setSku((String)m.get("sku"));
        setExpiryDate((Date)m.get("expiryDate"));
        cancellationDate = (Date)m.get("cancellationDate");
        purchaseDate = (Date)m.get("purchaseDate");
        quantity = (Integer)m.get("quantity");
        transactionId = (String)m.get("transactionId");
        orderData = (String)m.get("orderData");
        storeCode = (String)m.get("storeCode");
        internalId = (String)m.get("internalId");
    }

    /**
     * {@inheritDoc}
     * @return 
     */
    public String getObjectId() {
        return "com.codename1.payment.Receipt";
    }
    
    /**
     * Checks whether we have registered the externalizable yet.
     * @see #registerExternalizable() 
     * @return 
     */
    static boolean isExternalizableRegistered() {
        return externalizableRegistered;
    }
    
    /**
     * Registers this class as externalizable so that it can be serialized.
     */
    static void registerExternalizable() {
        if (!isExternalizableRegistered()) {
            Util.register("com.codename1.payment.Receipt", Receipt.class);
            setExternalizableRegistered(true);
        }
        
    }

    /**
     * Cancellation date of the receipt, if it has been cancelled.  A cancelled
     * receipt should be treated as if the purchase had never been made in the 
     * first place.
     * @return the cancellationDate
     */
    public Date getCancellationDate() {
        return cancellationDate;
    }

    /**
     * 
     * @param cancellationDate the cancellationDate to set
     * @see #getCancellationDate() 
     */
    public void setCancellationDate(Date cancellationDate) {
        this.cancellationDate = cancellationDate;
    }

    /**
     * Date of purchase.
     * @return the purchaseDate
     */
    public Date getPurchaseDate() {
        return purchaseDate;
    }

    /**
     * @param originalPurchaseDate the purchaseDate to set
     * @see #getPurchaseDate() 
     */
    public void setPurchaseDate(Date originalPurchaseDate) {
        this.purchaseDate = originalPurchaseDate;
    }



    /**
     * Quantity.  Currently this will always be 1, but potentially could be used
     * internally to denote other quantities.
     * @return the quantity
     */
    public int getQuantity() {
        return quantity;
    }

    /**
     * @param quantity the quantity to set
     * @see #getQuantity() 
     */
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    /**
     * The store-specific transaction ID.  This will match exactly the transaction
     * id of the store the issued the receipt.  Use the {@code storeCode} to differentiate
     * between transactions of different stores, which may potentially have the 
     * same transaction Ids.
     * @return the transactionId
     */
    public String getTransactionId() {
        return transactionId;
    }

    /**
     * @param transactionId the transactionId to set
     * @see #getTransactionId() 
     */
    public void setTransactionId(String transactionId) {
        this.transactionId = transactionId;
    }


    /**
     * The order data that may be used to verify the receipt with the appropriate
     * REST service.  This data will be in different formats for different stores.
     * 
     * <p>iTunes has a base64 encoded string, Play has a JSON structure, Windows
     * has an XML string, etc...</p>
     * 
     * <p>This will be filled in at the time that the receipt is generated by the
     * platform's implementation, so developers should never have to explicitly 
     * set this.</p>
     * @return the orderData
     */
    public String getOrderData() {
        return orderData;
    }

    /**
     * @param orderData the orderData to set
     * @see #getOrderData() 
     */
    public void setOrderData(String orderData) {
        this.orderData = orderData;
    }

    /**
     * The store code from which this receipt originated.  Will usually be one of
     * {@link #STORE_CODE_ITUNES}, {@link #STORE_CODE_PLAY}, {@link #STORE_CODE_WINDOWS},
     * or {@link #STORE_CODE_SIMULATOR}, but developers could also use custom codes
     * if they use a different store, or no store at all.  This code can be used
     * for validating the receipt.
     * @return the storeCode
     */
    public String getStoreCode() {
        return storeCode;
    }

    /**
     * @param storeCode the storeCode to set
     * @see #getStoreCode() 
     */
    public void setStoreCode(String storeCode) {
        this.storeCode = storeCode;
    }

    /**
     * An internal ID that may be optionally used to link the receipt to an internal
     * order ID.
     * @return the internalId
     */
    public String getInternalId() {
        return internalId;
    }

    /**
     * @param internalId the internalId to set
     * @see #getInternalId() 
     */
    public void setInternalId(String internalId) {
        this.internalId = internalId;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Receipt {")
                .append("sku:").append(sku).append(", ")
                .append("expiryDate:").append(expiryDate).append(", ")
                .append("cancellationDate:").append(cancellationDate).append(", ")
                .append("purchaseDate:").append(purchaseDate).append(", ")
                .append("orderData:").append(orderData).append(", ")
                .append("quantity:").append(quantity).append(", ")
                .append("transactionId:").append(transactionId).append(", ")
                .append("storeCode:").append(storeCode).append(", ")
                .append("internalId:").append(internalId).append("}");
        return sb.toString();
    }

    
    
}
